#include "plc2llvm/Semantic/SemanticError.h"
#include <regex>

const char * SemanticError::what () const throw (){
    if(errorMsg.empty()){
        return "no semantic error message found.";
    }else{
        return this->errorMsg.c_str();
    }
}

SemanticError::SemanticError(const antlr4::Token* symbol, std::string msg) {
    // error row
    auto line = symbol->getLine();
    // error col
    auto charPositionInLine = symbol->getCharPositionInLine();

    //append "row:line msg\n" to errormsg
    this->errorMsg.append(std::to_string(line));
    this->errorMsg.append(":");
    this->errorMsg.append(std::to_string(charPositionInLine));
    this->errorMsg.append(" ");
    this->errorMsg.append(msg);
    this->errorMsg.append("\n");

    // get source code lines
    auto input = symbol->getInputStream()->toString();
    std::regex newline_re("\n");
	std::vector<std::string> lines(
        std::sregex_token_iterator(input.begin(), input.end(), newline_re, -1), 
        std::sregex_token_iterator());
    
    // append error line and underline to error msg
    auto errorLine = lines[line - 1];
    this->errorMsg.append(errorLine);
    this->errorMsg.append("\n");
    for(int i = 0; i < charPositionInLine; i++){
        this->errorMsg.append(" ");
    }
    int start = symbol->getStartIndex();
    int stop = symbol->getStopIndex();
    if(start >= 0 && stop >= 0){
        for(int i = start; i <= stop; i++){
            this->errorMsg.append(UNDERLINE);
        }
    }
    this->errorMsg.append("\n");
}

/*
    获取tokens -> underlineToken
*/
SemanticError::SemanticError(const antlr4::TokenStream* tokens, const antlr4::ParserRuleContext* ctx, std::string msg) {
    auto start = ctx->start;
    auto start_line = start->getLine();
    auto charPositionInLine = start->getCharPositionInLine();

    //append "row:line msg\n" to errormsg
    this->errorMsg.append(std::to_string(start_line));
    this->errorMsg.append(":");
    this->errorMsg.append(std::to_string(charPositionInLine));
    this->errorMsg.append(" ");
    this->errorMsg.append(msg);
    this->errorMsg.append("\n");

    // get source code lines
    auto input = start->getInputStream()->toString();
    std::regex newline_re("\n");
	std::vector<std::string> lines(
        std::sregex_token_iterator(input.begin(), input.end(), newline_re, -1), 
        std::sregex_token_iterator());

    // append error line and underline to error msg
    auto start_index = start->getTokenIndex();
    auto stop_index = ctx->stop->getTokenIndex();

    int last_tail = 0;
    int token_line;

    // insert first line
    auto errorLine = lines[start_line - 1];
    this->errorMsg.append(errorLine);
    this->errorMsg.append("\n");

    for(int i = start_index; i <= stop_index; i++){
        auto token = tokens->get(i);
        token_line = token->getLine();
        if(token_line != start_line){ //换行
            auto errorLine = lines[token_line - 1];
            this->errorMsg.append("\n");
            this->errorMsg.append(errorLine);
            this->errorMsg.append("\n");
            start_line = token_line;
            last_tail = 0;
        }
        int tokenPosInLine = token->getCharPositionInLine();
        int token_length = token->getStopIndex() - token->getStartIndex() + 1;
        for(int j = last_tail; j < tokenPosInLine; j++){
            this->errorMsg.append(" ");
        }
        for(int k = 0; k <= token_length - 1; k++){
            this->errorMsg.append(UNDERLINE);
        }
        last_tail = tokenPosInLine + token_length;
    }
}

SemanticError::SemanticError(std::string msg){
    this->errorMsg.append(msg);
}